#pragma once
#include "InetAddr.hpp"
#include "Log.hpp"

using namespace inetaddr;

namespace sock
{
    enum
    {
        SOCKERROR,
        BINDERROR,
        CONNECTERROR,
        WRITEERROR,
        LISTENERROR,
        ACCEPTERROR
    };
    const int backlog = 15;

    class Socket
    {
    public:
        virtual int CreateSocket() = 0;
        virtual int BindSocket() = 0;
        virtual int Accept(sockaddr_in *ppeer, socklen_t *ppeer_len) = 0;
        virtual int Connect(sockaddr_in server, socklen_t len) = 0;
        virtual int Recv(std::string *out) = 0;
        virtual int Send(const std::string &message) = 0;
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(std::string ip, uint16_t port)
            : _ip(ip), _port(port)
        {
        }

        TcpSocket(int sockfd)
            : _sockfd(sockfd)
        {
        }

        int CreateSocket() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(FATAL, "create socket fail\n");
                return SOCKERROR;
            }
            LOG(INFO, "create socket success");
            // 端口重复使用
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

            return _sockfd;
        }

        int BindSocket() override
        {
            sockaddr_in server = InetAddr(_ip, _port).getsocket();
            socklen_t len = sizeof(server);
            int n = bind(_sockfd, (sockaddr *)&server, len);
            if (n < 0)
            {
                LOG(FATAL, "bind fail\n");
                return BINDERROR;
            }
            LOG(INFO, "bind success");

            return n;
        }

        int Listen()
        {
            int n = listen(_sockfd, backlog);
            if (n < 0)
            {
                LOG(FATAL, "listen fail\n");
                return LISTENERROR;
            }
            LOG(INFO, "listen begin...");

            return n;
        }

        int Accept(sockaddr_in *ppeer, socklen_t *ppeer_len) override
        {
            int sockfd = accept(_sockfd, (sockaddr *)ppeer, ppeer_len);
            if(sockfd < 0)
            {
                LOG(ERROR, "accept fail\n");
                return ACCEPTERROR;
            }

            return sockfd;
        }

        int Connect(sockaddr_in server, socklen_t len) override
        {
            int n = connect(_sockfd, (sockaddr *)&server, len);
            if (n < 0)
            {
                LOG(ERROR, "connect fail\n");
                return CONNECTERROR;
            }
            return n;
        }

        int Recv(std::string *out) override
        {
            char buffer[10240];
            ssize_t n = read(_sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = '\0';
                LOG(INFO, "recv a message: %s", buffer);
                *out += buffer;
            }
            return n;
        }

        int Send(const std::string &message) override
        {
            ssize_t n = write(_sockfd, message.c_str(), message.size());
            if (n < 0)
            {
                LOG(FATAL, "write fail\n");
                return WRITEERROR;
            }
            return 0;
        }

        int GetSockfd()
        {
            return _sockfd;
        }

        void Close()
        {
            close(_sockfd);
        }

    private:
        int _sockfd;
        std::string _ip;
        uint16_t _port;
    };
}